package com.homedo.microservice.open.service.gateway.zuul.routelocator;

import com.google.common.collect.Lists;
import com.homedo.common.util.Detect;
import com.homedo.microservice.open.service.gateway.bean.po.enhanced.RouteEnhanced;
import com.homedo.microservice.open.service.gateway.zuul.conf.RouteConf;
import com.homedo.microservice.open.service.gateway.zuul.constant.ZuulConstant;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.discovery.ServiceRouteMapper;

import java.util.List;
import java.util.Map;

public class LocalRouteLocator extends DiscoveryClientRouteLocator {
    private static final Logger LOGGER = LoggerFactory.getLogger(LocalRouteLocator.class);

    private ZuulProperties properties;

    public LocalRouteLocator(String servletPath, DiscoveryClient discovery, ZuulProperties properties, ServiceInstance localServiceInstance) {
        super(servletPath, discovery, properties, localServiceInstance);
        this.properties = properties;
    }

    public LocalRouteLocator(String servletPath, DiscoveryClient discovery, ZuulProperties properties, ServiceRouteMapper serviceRouteMapper, ServiceInstance localServiceInstance) {
        super(servletPath, discovery, properties, serviceRouteMapper, localServiceInstance);
        this.properties = properties;
    }

    public void resetRoutes() {
        this.properties.getRoutes().clear();
        getRoutesMap().clear();
    }

    @Override
    public Route getMatchingRoute(final String path) {

        RequestContext requestContext = RequestContext.getCurrentContext();
        Route forceRoute = (Route) requestContext.get(ZuulConstant.ZUUL_ROUTE_USE_FORCE_ROUTE);
        if (forceRoute != null) {
            return forceRoute;
        }

        String addPrefixPath = path;
        String routeAddPrefix = (String) requestContext.get(ZuulConstant.ZUUL_ROUTE_ADD_PREFIX);
        if (Detect.notEmpty(routeAddPrefix)) {
            addPrefixPath = "/" + routeAddPrefix + ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT_PATH + this.adjustPath(addPrefixPath);
        }

        if (requestContext.debugRequest()) {
            Map<String, ZuulProperties.ZuulRoute> routesMap = getRoutesMap();
            LOGGER.info("[当前生效路由执行顺序:{},当前地址:{}]", routesMap.keySet(), addPrefixPath);
        }

        Route route = super.getMatchingRoute(addPrefixPath);
        if (route == null || ZuulConstant.DEFAULT_SERVICE_ID.equalsIgnoreCase(route.getLocation())) {
            return this.getMatchingRoute4hasPrefix(addPrefixPath);
        }

        if (route != null && !route.isPrefixStripped()) {//不用去除路由前缀时 需要把@分隔符前面的部分去掉
            int index = route.getPath().indexOf(ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT_PATH);
            if (index > -1) {
                route.setPath("/" + route.getPath().substring(index + 1));
            }
        }

        if (route != null) {
            if (route.getPath().startsWith(ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT_PATH)) {
                route.setPath(route.getPath().substring(1));
//                route.setPath("");
            }

            RouteEnhanced routeEnhanced = RouteConf.getInstance().getConfs().get(route.getId());
            if (routeEnhanced != null && Detect.notEmpty(routeEnhanced.getAddPrefix())) {
                if (route.getPath().startsWith("/")) {
                    route.setPath(routeEnhanced.getAddPrefix() + route.getPath());
                } else {
                    route.setPath(routeEnhanced.getAddPrefix() + "/" + route.getPath());
                }
            }
        }

        return route;
    }

    private Route getMatchingRoute4hasPrefix(String addPrefixPath) {
        Route route = null;

        RequestContext requestContext = RequestContext.getCurrentContext();

        List<String> curPathCache = Lists.newArrayList();

        Boolean isAccessedRouteDomainFilter = (Boolean) requestContext.get(ZuulConstant.ZUUL_IS_ACCESSED_ROUTE_DOMAIN_FILTER);
        Boolean isAccessedVersionPreFilter = (Boolean) requestContext.get(ZuulConstant.ZUUL_IS_ACCESSED_ROUTE_VERSION_FILTER);

        if (isAccessedVersionPreFilter != null && isAccessedVersionPreFilter) {
            int versionIndex = 0;
            if (isAccessedRouteDomainFilter != null && isAccessedRouteDomainFilter) {
                versionIndex = 1;
            }

            route = this.getMatchingRoute4hasPrefix(versionIndex, addPrefixPath, curPathCache);

            if (route != null && !ZuulConstant.DEFAULT_SERVICE_ID.equalsIgnoreCase(route.getLocation())) {
                return route;
            }
        }

        if (isAccessedRouteDomainFilter != null && isAccessedRouteDomainFilter) {
            if (curPathCache.size() > 0) {
                addPrefixPath = curPathCache.get(0);
            }

            curPathCache.clear();

            int domainIndex = 0;
            route = this.getMatchingRoute4hasPrefix(domainIndex, addPrefixPath, curPathCache);

            if (route != null && !ZuulConstant.DEFAULT_SERVICE_ID.equalsIgnoreCase(route.getLocation())) {
                return route;
            }
        }

        return route;

    }

    private Route getMatchingRoute4hasPrefix(int excludIndex, String addPrefixPath, List<String> curPathCache) {
        if (addPrefixPath.indexOf(ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT_PATH) < 0) {
            return null;
        }

        String[] pathSplit = addPrefixPath.split(ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT_PATH);
        String[] prefixSplit = pathSplit[0].split(ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT);

        String curPath = "";

        for (int i = 0; i < prefixSplit.length; i++) {
            if (i != excludIndex) {
                curPath += prefixSplit[i] + ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT;
            }
        }

        if (curPath.indexOf(ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT) > -1) {
            curPath = curPath.substring(0, curPath.lastIndexOf(ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT));
        }

        if (Detect.notEmpty(curPath)) {
            curPath = curPath + ZuulConstant.ZUUL_ROUTE_ADD_PREFIX_SPLIT_PATH + pathSplit[1];
        } else {
            curPath = pathSplit[1];
        }
        if (!curPath.startsWith("/")) {
            curPath = "/" + curPath;
        }

        curPathCache.add(curPath);
        return super.getMatchingRoute(curPath);
    }

    private String adjustPath(String path) {
        if (path.startsWith("/")) {
            return this.adjustPath(path.substring(1));
        }
        return path;
    }


}
